package com.pan.download5.parse;

import com.pan.download5.config.LineConfig;
import com.pan.download5.parse.entity.auto.impl.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;

public class AutoXmlParser {



    public AutoXmlParser(String path) {
        parse(path);
    }

//    private static AutoCrawlerConfig instance;
//    private static AtomicInteger flag = new AtomicInteger(1);
//    public static AutoCrawlerConfig getAutoCrawlerConfig(){
//        for (;!flag.compareAndSet(1,0););
//        try {
//            if (instance == null){
//                instance = new AutoCrawlerConfig();
//            }
//        }finally {
//            for (;!flag.compareAndSet(0,1););
//        }
//        return instance;
//    }

    private void parse(String crawlerXmlPath){
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder;
        try {
            documentBuilder = documentBuilderFactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        }
        URL fileUrl = LineConfig.class.getClassLoader().getResource(crawlerXmlPath);
        String filePath = fileUrl.getFile();
        try {
            filePath = URLDecoder.decode(filePath, Charset.defaultCharset().toString());
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        Document crawlerDoc = null;
        try {
            crawlerDoc = documentBuilder.parse(new File(filePath));
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        crawlerDoc.normalizeDocument();
        Element rootElement = crawlerDoc.getDocumentElement();


        //
        XPathFactory xPathFactory = XPathFactory.newInstance();
        XPath xPath = xPathFactory.newXPath();

        BiFunction<Element ,String, Element> getElementByXpath = (elm, xp)->{
            try {
                XPathExpression xPathExpression = xPath.compile(xp);
                Element reuslt = (Element) xPathExpression.evaluate(elm, XPathConstants.NODE);
                return reuslt;
            } catch (XPathExpressionException ex) {
                throw new RuntimeException(ex);
            }
        };
        Function<String,Element> getElementFromRootByXpath = xp-> getElementByXpath.apply(rootElement, xp);

        BiFunction<Element, String, String> getElementTextByXpath = (elm,xp) -> {
            Element e = getElementByXpath.apply(elm, xp);
            return e==null?null: e.getTextContent().trim();
        };
        Function<String,String> getElementTextFromRootByXpath = xp -> {
            Element element = getElementByXpath.apply(rootElement, xp);
            return element == null? null: element.getTextContent().trim();
        };

        BiFunction<String[],Element, Map<String, Element>> mapXml = (memberArr,elm) -> {
            Map<String, Element> result = new HashMap<>();
            for (String memberName: memberArr){
                String xpathPrefix = memberName;
                Element e = getElementByXpath.apply(elm, xpathPrefix);
                result.put(memberName, e);
            }
            return result;
        };
        BiFunction<Element,String[], Map<String, String>> mapXmlStr = (elm,memberXpathArr) -> {
            Map<String, String> result = new HashMap<>();
            for (String memberName: memberXpathArr){
                Element e = getElementByXpath.apply(elm, memberName);
                result.put(memberName, e==null?null: e.getTextContent().trim());
            }
            return result;
        };

        //获取website
        String websiteXPath =  "//pan/config/website";
        this.website = getElementTextFromRootByXpath.apply(websiteXPath);

        //获取targetUrl
        String crawlerTargetXPath = "//pan/config/crawler-target";
        this.targetUrl = getElementTextFromRootByXpath.apply(crawlerTargetXPath);

        //解析title
        String titleXPath = "//pan/title";
        Element titleElement = getElementFromRootByXpath.apply(titleXPath);
        if (titleElement == null){
            //暂时不抛出错误
            this.title = null;
        }else {
            this.title = new AutoTitle();
            String xxp = "xpath";
            String cxp = "clear";
            String x = getElementTextByXpath.apply(titleElement, xxp);
            String c = getElementTextByXpath.apply(titleElement, cxp);
            title.setXpath(x);
            title.setClearRegex(c);
        }

        //解析directory
        //
        String directoryXPath = "//pan/directory";
        Element directoryElement = getElementFromRootByXpath.apply(directoryXPath);
        if (directoryElement == null){
            this.directory = null;
        }else {
            this.directory = new AutoDirectory();
            String directoryXpathStr = "xpath";
            String directoryItemStr = "item";
            String directoryNextStr = "next" ;
            Map<String, Element>  directoryMap = mapXml.apply(new String[]{directoryItemStr, directoryXpathStr, directoryNextStr},directoryElement );
            //xpath
            this.directory.setXpath(directoryMap.get(directoryXpathStr) == null? null: directoryMap.get(directoryXpathStr).getTextContent().trim());
            //item
            AutoItem autoItem;
            if (directoryMap.get(directoryItemStr) == null){
                autoItem = null;
            }else {
                Element itemElement = directoryMap.get(directoryItemStr);
                autoItem = new AutoItem();
                String dix = "xpath";
                String dic = "chap";
                String diu = "url";
                Map<String,Element> itemElementMap = mapXml.apply(new String[]{dix, dic, diu} ,itemElement);
                autoItem.setXpath(itemElementMap.get(dix)==null?null: itemElementMap.get(dix).getTextContent().trim());
                Element chapElement = itemElementMap.get(dic);
                if(chapElement == null){
                    autoItem.setChap(null);
                }else {
                    String chapMember[] = {"xpath","value", "clear"};
                    Map<String, String> chapMap = mapXmlStr.apply(chapElement, chapMember);
                    AutoValue chap = new AutoValue(
                            chapMap.get("xpath"),
                            chapMap.get("clear"),
                            chapMap.get("value"));
                    autoItem.setChap(chap);
                }
                Element urlElement = itemElementMap.get(diu);
                if (urlElement == null){
                    autoItem.setUrl(null);
                }else {
                    String urlMember[] = {"xpath","value", "clear"};
                    Map<String, String> uMap = mapXmlStr.apply(urlElement, urlMember);
                    AutoValue u = new AutoValue(
                            uMap.get("xpath"),
                            uMap.get("clear"),
                            uMap.get("value"));
                    autoItem.setUrl(u);
                }
            }
            this.directory.setItem(autoItem);

            //item next
            AutoNext autoNext;
            Element nextElement = directoryMap.get("next");
            if (nextElement == null){
                autoNext = null;
            }else {
                String[] nextArr = new String[]{"xpath", "condition", "click", "clear"};
                Map<String, String> nextMap = mapXmlStr.apply(nextElement, nextArr);
                autoNext = new AutoNext(
                        nextMap.get("condition"),
                        nextMap.get("click"),
                        nextMap.get("xpath"),
                        nextMap.get("clear"));
            }
            directory.setNext(autoNext);
        }


        Element pageElement = getElementFromRootByXpath.apply("//pan/page");
        if (pageElement == null){
            this.page = null;
        }else {
            Map<String, String> xcMap = mapXmlStr.apply(pageElement, new String[]{"xpath", "clear"});
            this.page = new AutoPage();
            this.page.setXpath(xcMap.get("xpath"));
            this.page.setClearRegex(xcMap.get("clear"));
            Element nextElement = getElementByXpath.apply(pageElement, "next");

            AutoNext autoNext;
            if (nextElement == null){
                autoNext = null;
            }else {
                String[] nextArr = new String[]{"xpath", "condition", "click", "clear"};
                Map<String, String> nextMap = mapXmlStr.apply(nextElement, nextArr);
                autoNext = new AutoNext(
                        nextMap.get("condition"),
                        nextMap.get("click"),
                        nextMap.get("xpath"),
                        nextMap.get("clear"));
            }
            page.setNext(autoNext);
        }



    }
    private String website;
    public String getWebsite() {return website;}

    private String targetUrl;
    public String getTargetUrl() {return targetUrl;}

    private AutoTitle title;
    public AutoTitle getTitle(){return this.title;}

    private AutoDirectory directory;
    public AutoDirectory getDirectory(){return directory;}

    private AutoPage page;
    public AutoPage getPage() {return page;}

//    public static void main(String[] args) {
//        AutoCrawlerConfig autoCrawlerConfig = AutoCrawlerConfig.getAutoCrawlerConfig();
//        System.out.println(autoCrawlerConfig.getWebsite());
//        System.out.println(autoCrawlerConfig.getTargetUrl());
//        System.out.println(autoCrawlerConfig.getTitle().toString());
//        System.out.println(autoCrawlerConfig.getDirectory());
//        System.out.println(autoCrawlerConfig.getPage());
//    }

}
